#ifndef _SYNC_OBJECTS_H
#define _SYNC_OBJECTS_H

#include "windows.h"

//////////////////////////////////////////////////////////////////////
class ILockObj
{
public:
	virtual void Lock	(void)	= 0;
	virtual void Unlock	(void)	= 0;
};


//////////////////////////////////////////////////////////////////////
class CMyCriticalSection : public ILockObj
{
public:
    CMyCriticalSection( /*DWORD spincount = 2000*/ )
    {
        //InitializeCriticalSectionAndSpinCount( &m_CritSec , spincount);
		InitializeCriticalSection( &m_CritSec );
	};

    virtual ~CMyCriticalSection()
    {
        DeleteCriticalSection( &m_CritSec );
    };

    void    Lock(void)
    {
        EnterCriticalSection( &m_CritSec );
    };

    void    Unlock(void)
    {
        LeaveCriticalSection( &m_CritSec );
    };

private:
    CRITICAL_SECTION    m_CritSec;
};


//////////////////////////////////////////////////////////////////////
class CMyMutex : public ILockObj
{
public:
	CMyMutex() : m_hMutex(NULL) {}
	~CMyMutex(void)	{ if (m_hMutex) ::CloseHandle(m_hMutex); }

	void Lock	(void)	{ if (m_hMutex) ::WaitForSingleObject(m_hMutex, INFINITE); }
	void Unlock	(void)	{ if (m_hMutex) ::ReleaseMutex(m_hMutex); }

private:
	HANDLE m_hMutex;

// static
public:
	static CMyMutex* CreateNew(const char* pszName = NULL, bool bExistTest = false)
	{
		HANDLE hMutex = ::CreateMutex(NULL, FALSE, pszName);
		if (!hMutex)
			return NULL;

		if (pszName && bExistTest)
		{
			if (ERROR_ALREADY_EXISTS == ::GetLastError())
			{
				::CloseHandle(hMutex);
				return NULL;
			}
		}


		CMyMutex* pMutex = new CMyMutex;
		if (!pMutex)
			return NULL;

		pMutex->m_hMutex = hMutex;
		return pMutex;
	}
};

//////////////////////////////////////////////////////////////////////
class CMySingleLock 
{
public:
	CMySingleLock(ILockObj& lock)
		: m_lock(lock)
	{ 		
		m_lock.Lock(); 
	}

	~CMySingleLock	(void)	
	{
		m_lock.Unlock();
	}

private:
	ILockObj&	m_lock;
	void operator = (CMySingleLock& /*opr*/)	{ return ;}
};


//////////////////////////////////////////////////////////////////////
class IRWLockObj
{
public:
	virtual void LockRead	(void)		= 0;
	virtual void LockWrite	(void)		= 0;
	virtual void Unlock		(void)		= 0;
};


//////////////////////////////////////////////////////////////////////
// One writer / multi reader lock
class CMyRWLock : public IRWLockObj
{
public:
	CMyRWLock()
	{
		m_nWaitingReaders = m_nWaitingWriters = m_nActive = 0;
		m_hsemReaders = CreateSemaphore(NULL, 0, MAXLONG, NULL);
		m_hsemWriters = CreateSemaphore(NULL, 0, MAXLONG, NULL);
		InitializeCriticalSection(&m_CritSec);
	}
	~CMyRWLock()
	{
#ifdef _DEBUG
		// a rwlock shouldn't be destroyed if any threads a using the resource
		if (0 != m_nActive)
			DebugBreak();
#endif
        DeleteCriticalSection( &m_CritSec );
		CloseHandle(m_hsemReaders);
		CloseHandle(m_hsemWriters);
	}

public:
	virtual void LockRead	(void)
	{
		EnterCriticalSection(&m_CritSec);
		// are there writers waiting or is a writer writing?
		bool bResourceWritePending = (m_nWaitingWriters || (m_nActive < 0));
		if (bResourceWritePending)
			++m_nWaitingReaders;	// this reader must wait
		else
			++m_nActive;			// this reader can read
		LeaveCriticalSection(&m_CritSec);
		if (bResourceWritePending)
			::WaitForSingleObject(m_hsemReaders, INFINITE);	// must wait
	}
	virtual void LockWrite	(void)
	{
		EnterCriticalSection(&m_CritSec);
		// any one accessing?
		bool bResourceOwned = (m_nActive != 0);
		if (bResourceOwned)
			++m_nWaitingWriters;	// this writer must wait
		else
			m_nActive = -1;			// this writer can write
		LeaveCriticalSection(&m_CritSec);
		if (bResourceOwned)
			WaitForSingleObject(m_hsemWriters, INFINITE);	// must wait
	}
	virtual void Unlock		(void)
	{
		EnterCriticalSection(&m_CritSec);
		if (m_nActive > 0)
			--m_nActive;		// a reader done
		else
			++m_nActive;		// a writer done
		HANDLE hsem = NULL;		// assume no one are waiting
		long lCount = 1;		// assume only 1 waiter wakes; always true for writers
		if (0 == m_nActive)
		{
			// no one has access, who should wake up?
			// NOTE:	writer first, it is possible that readers could never get access
			//			if there are always writers waiting to write
			if (m_nWaitingWriters > 0)
			{
				m_nActive = -1;			// a writer will get access
				--m_nWaitingWriters;	// one less writer will be waiting
				hsem = m_hsemWriters;	// writers wait on this semaphore
			} else if (m_nWaitingReaders > 0) {
				m_nActive = m_nWaitingReaders;	// all readers will get access
				m_nWaitingReaders = 0;			// no readers will be waiting
				hsem = m_hsemReaders;			// readers wait on this semaphore
				lCount = m_nActive;				// semaphore release all readers
			}
		}
		LeaveCriticalSection(&m_CritSec);
		if (NULL != hsem)
			ReleaseSemaphore(hsem, lCount, NULL);
	}

private:
    CRITICAL_SECTION    m_CritSec;
	HANDLE		m_hsemReaders;			// readers wait on this if a writer has access
	HANDLE		m_hsemWriters;			// writers wait on this if a reader has access
	int			m_nWaitingReaders;		// number of readers waiting for access
	int			m_nWaitingWriters;		// number of writers waiting for access
	int			m_nActive;				// number of threads current with access
										// (0=no threads, >0=# of threads, -1=1 writer)
};


//////////////////////////////////////////////////////////////////////
class CMyReadLock 
{
public:
	CMyReadLock(IRWLockObj& lock)
		: m_lock(lock)
	{ 		
		m_lock.LockRead(); 
	}
	
	~CMyReadLock	(void)	
	{
		m_lock.Unlock();
	}
	
private:
	IRWLockObj&	m_lock;
	void operator = (const CMyReadLock& /*opr*/)	{ return ; }
};

//////////////////////////////////////////////////////////////////////
class CMyWriteLock 
{
public:
	CMyWriteLock(IRWLockObj& lock)
		: m_lock(lock)
	{ 		
		m_lock.LockWrite(); 
	}
	
	~CMyWriteLock	(void)	
	{
		m_lock.Unlock();
	}
	
private:
	IRWLockObj&	m_lock;
	void operator = (const CMyWriteLock& /*opr*/)	{ return ; }
};


#endif
